﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using Catel.Data;
using Catel.MVVM.Services;
using nsoftware.IPWorks;

namespace SoundSampleReceiver.ViewModels
{
    using Catel.MVVM;

    /// <summary>
    /// MainWindow view model.
    /// </summary>
    public class MainWindowViewModel : ViewModelBase
    {
        #region Fields
        private List<FileStream> _fileStreams;
        private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="MainWindowViewModel"/> class.
        /// </summary>
        public MainWindowViewModel()
            : base()
        {
            Start = new Command(OnStartExecute, OnStartCanExecute);
            Stop = new Command(OnStopExecute, OnStopCanExecute);
            ErrorMessage = "就绪";

            //OnStartExecute();
        }

        private bool OnStartCanExecute()
        {
            return !UdpStarted;
        }

        #endregion

        #region Properties
        /// <summary>
        /// Gets the title of the view model.
        /// </summary>
        /// <value>The title.</value>
        public override string Title { get { return "阵列信号采集接收器"; } }

        // TODO: Register models with the vmpropmodel codesnippet
        // TODO: Register view model properties with the vmprop or vmpropviewmodeltomodel codesnippets

        /// <summary>
        /// Gets or sets the property value.
        /// </summary>
        public string ErrorMessage
        {
            get { return GetValue<string>(ErrorMessageProperty); }
            set { SetValue(ErrorMessageProperty, value); }
        }

        /// <summary>
        /// Register the ErrorMessage property so it is known in the class.
        /// </summary>
        public static readonly PropertyData ErrorMessageProperty = RegisterProperty("ErrorMessage", typeof(string), null);

        /// <summary>
        /// Gets or sets the property value.
        /// </summary>
        public bool UdpStarted
        {
            get { return GetValue<bool>(UdpStartedProperty); }
            set { SetValue(UdpStartedProperty, value); }
        }

        /// <summary>
        /// Register the UdpStarted property so it is known in the class.
        /// </summary>
        public static readonly PropertyData UdpStartedProperty = RegisterProperty("UdpStarted", typeof(bool), false);

        /// <summary>
        /// Gets or sets the property value.
        /// </summary>
        public int PacketsReceived
        {
            get { return GetValue<int>(PacketsReceivedProperty); }
            set { SetValue(PacketsReceivedProperty, value); }
        }

        /// <summary>
        /// Register the PacketsReceived property so it is known in the class.
        /// </summary>
        public static readonly PropertyData PacketsReceivedProperty = RegisterProperty("PacketsReceived", typeof(int), 0);


        /// <summary>
        /// port.
        /// </summary>
        public int Port
        {
            get { return GetValue<int>(PortProperty); }
            set { SetValue(PortProperty, value); }
        }

        /// <summary>
        /// Register the Port property so it is known in the class.
        /// </summary>
        public static readonly PropertyData PortProperty = RegisterProperty("Port", typeof(int), 1000);

        private Udpport _udp;
        private const byte FrameHeader = 0xaa;
        private const int ChannelsCount = 16;
        private const int FramesCount = 24;
        private const int BytesPerChannel = 3;

        #endregion

        #region Commands
        // TODO: Register commands with the vmcommand or vmcommandwithcanexecute codesnippets
        /// <summary>
        /// Gets the Stop command.
        /// </summary>
        public Command Stop { get; private set; }



        /// <summary>
        /// Method to check whether the Stop command can be executed.
        /// </summary>
        /// <returns><c>true</c> if the command can be executed; otherwise <c>false</c></returns>
        private bool OnStopCanExecute()
        {
            return UdpStarted;
        }

        /// <summary>
        /// Method to invoke when the Stop command is executed.
        /// </summary>
        private void OnStopExecute()
        {
            _udp.Active = false;
            UdpStarted = false;
            CloseFileStreams();
        }

        private void CloseFileStreams()
        {
            if (_fileStreams != null)
            {
                _fileStreams.ForEach(f=>f.Close());
                _fileStreams = null;
            }
        }

        /// <summary>
        /// Gets the Start command.
        /// </summary>
        public Command Start { get; private set; }



        /// <summary>
        /// Method to invoke when the Start command is executed.
        /// </summary>
        private void OnStartExecute()
        {
            // TODO: Handle command logic here
            try
            {
                if (_udp == null)
                {
                    _udp = new Udpport();
                    _udp.Config("InBufferSize=2048");
                    _udp.Config("MaxPacketSize=2048");
                    _udp.LocalPort = Port;
                    _udp.OnError += UdpOnOnError;
                    _udp.OnDataIn += UdpOnOnDataIn;
                }
                _udp.Active = true;
                UdpStarted = true;
            }
            catch (Exception ex)
            {
                GetService<IMessageService>().ShowError(ex);
            }
            
        }

        private void UdpOnOnError(object sender, UdpportErrorEventArgs udpportErrorEventArgs)
        {
            var msg = GetService<IDispatcherService>();
            msg.BeginInvoke(() => ErrorMessage = udpportErrorEventArgs.Description);
        }

        private void UdpOnOnDataIn(object sender, UdpportDataInEventArgs udpportDataInEventArgs)
        {
            var dispatcherService = GetService<IDispatcherService>();
            dispatcherService.BeginInvoke(()=> PacketsReceived++);

            try
            {
                var msg = string.Empty;
                var valid = IsValidPacket(udpportDataInEventArgs.DatagramB, out msg);
                dispatcherService.BeginInvoke(() => ErrorMessage = msg);
                if (!valid) return;
                
                if (_fileStreams == null)
                {
                    _fileStreams = CreateFileStreams(DateTime.Now);
                }
                
                WriteBytes(new List<Stream>(_fileStreams), udpportDataInEventArgs.DatagramB);
            }
            catch (IOException ioException)
            {
                Stop.Execute();
                Stop.RaiseCanExecuteChanged();
                Start.RaiseCanExecuteChanged();
                dispatcherService.BeginInvoke(()=>ErrorMessage=ioException.Message);
            }
        }

        public static void WriteBytes(List<Stream> streams, byte[] bytes)
        {
            for (int i = 0; i < FramesCount; i++)
            {
                for (int j = 0; j < ChannelsCount; j++)
                {
                    int offset = i*(BytesPerChannel*ChannelsCount + 1) +
                                 j*BytesPerChannel + 1;

                    streams[j].Write(bytes, offset, 3);
                }
            }
            
        }

        public static List<FileStream> CreateFileStreams(DateTime dateTime)
        {
            
                var fileStreams = new List<FileStream>();
                for (int i = 0; i < ChannelsCount; i++)
                {
                    var fileName = string.Format(@"data\{0:d2}-{1:MM.dd HH_mm_ss}.dat", i + 1, dateTime);
                    var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
                    var dir = Path.GetDirectoryName(path);
                    if (!Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }

                    var fs = File.OpenWrite(path);
                    fileStreams.Add(fs);
                }
           
                return fileStreams;

        }

        

        #endregion

        #region Methods
        // TODO: Create your methods here

        public static bool IsValidPacket(byte[] data, out string errorMessage)
        {
            const int len = FramesCount*(ChannelsCount*BytesPerChannel + 1);
            if (data.Length != len)
            {
                errorMessage = string.Format("数据包长度不正确: len = {0} ", data.Length);
                return false;
            }

            for (int i = 0; i < FramesCount; i++)
            {
                var idx = i*(ChannelsCount*BytesPerChannel + 1);
                if (data[idx] != FrameHeader)
                {
                    errorMessage = string.Format("第{0}帧帧头非法： 帧头={1:x2}", i + 1, data[idx]);
                    return false;
                }
            }

            errorMessage = null;
            return true;
        }

        #endregion
    }
}
